<template>
  <el-form label-width="auto" style="max-width: 600px">
    <el-form-item label="请输入消息">
      <el-input v-model="messageText.content" type="textarea"/>
    </el-form-item>
    <el-form-item>
      <el-button type="primary" @click="sendMsg()">发送</el-button>
      <el-button type="danger" @click="disableWebSocket()">断开连接</el-button>
      <el-button type="success" @click="initWebSocket()">重新连接</el-button>
    </el-form-item>
  </el-form>


  <p>
    这个示例演示了在 WebRTC 中使用 DTMF。
  </p>

  <audio id="audio" autoplay controls></audio>
  <br/>
  <button name="dial" id="dial">拨号</button>

  <div class="log"></div>
</template>

<script setup>
import {onBeforeUnmount, onMounted, reactive, ref} from "vue";

let messageText = reactive({
  from: 1,
  to: 2,
  type: 0,
  groupId: null,
  content: ''
})

let receiveMsg = reactive({
  from: null,
  to: null,
  type: null,
  groupId: null,
  content: null
})

let websocket = null

const initWebSocket = () => {
  websocket = new WebSocket('ws://localhost:8004/webrtc');
  websocket.onopen = webSocketOnOpen
  websocket.onmessage = webSocketOnMessage
  websocket.onclose = webSocketOnClose
  websocket.onerror = webSocketOnError
}

const webSocketOnOpen = (event) => {
  console.log('建立连接')
  sendLoginMsg();
}

const webSocketOnMessage = (event) => {
  Object.assign(receiveMsg, JSON.parse(event.data))
  console.info("收到消息：" + receiveMsg.content);
}

const webSocketOnClose = (event) => {
  console.error('连接关闭');
}

const webSocketOnError = (event) => {
  console.error('连接错误');
}

const sendMsg = () => {
  messageText.type = 1
  websocket.send(JSON.stringify(messageText))
  console.info('发送消息：' + messageText.content)
}

const sendLoginMsg = () => {
  websocket.send(JSON.stringify(messageText))
}

const disableWebSocket = () => {
  messageText.type = -1
  messageText.content = null;
  websocket.send(JSON.stringify(messageText))
  websocket.close()
}


let dialString = "12024561111";
let callerPC = null;
let receiverPC = null;
let dtmfSender = null;
let hasAddTrack = false;

let mediaConstraints = {
  audio: true,
  video: false,
};

let offerOptions = {
  offerToReceiveAudio: 1,
  offerToReceiveVideo: 0,
};

let dialButton = null;
let logElement = null;

//启动连接过程
const connectAndDial = () => {
    callerPC = new RTCPeerConnection();
    hasAddTrack = callerPC.addTrack !== undefined;

    callerPC.onicecandidate = handleCallerIceEvent;
    callerPC.onnegotiationneeded = handleCallerNegotiationNeeded;
    callerPC.oniceconnectionstatechange = handleCallerIceConnectionStateChange;
    callerPC.onsignalingstatechange = handleCallerSignalingStateChangeEvent;
    callerPC.onicegatheringstatechange = handleCallerGatheringStateChangeEvent;

    receiverPC = new RTCPeerConnection();
    receiverPC.onicecandidate = handleReceiverIceEvent;

    if (hasAddTrack) {
      receiverPC.ontrack = handleReceiverTrackEvent;
    } else {
      receiverPC.onaddstream = handleReceiverAddStreamEvent;
    }

    navigator.mediaDevices
        .getUserMedia(mediaConstraints)
        .then(gotStream)
        .catch((err) => log(err.message));
}

//将音频添加到连接
const gotStream = (stream) => {
  log("已获取麦克风的访问权限。");

  let audioTracks = stream.getAudioTracks();

  if (hasAddTrack) {
    if (audioTracks.length > 0) {
      audioTracks.forEach((track) => callerPC.addTrack(track, stream));
    }
  } else {
    log(
        "你的浏览器不支持 RTCPeerConnection.addTrack()。正在退回到<strong>已弃用</strong>的 addStream() 方法…",
    );
    callerPC.addStream(stream);
  }

  if (callerPC.getSenders) {
    dtmfSender = callerPC.getSenders()[0].dtmf;
  } else {
    log(
        "你的浏览器不支持 RTCPeerConnection.getSenders()，因此正在退回到使用<strong>已弃用</strong>的 createDTMFSender()。",
    );
    dtmfSender = callerPC.createDTMFSender(audioTracks[0]);
  }

  dtmfSender.ontonechange = handleToneChangeEvent;
}

//当音调播放完成
const handleToneChangeEvent = (event) => {
  if (event.tone !== "") {
    log(`播放音调：${event.tone}`);
  } else {
    log("All tones have played. Disconnecting.");
    callerPC.getLocalStreams().forEach((stream) => {
      stream.getTracks().forEach((track) => {
        track.stop();
      });
    });
    receiverPC.getLocalStreams().forEach((stream) => {
      stream.getTracks().forEach((track) => {
        track.stop();
      });
    });

    audio.pause();
    audio.srcObject = null;
    receiverPC.close();
    callerPC.close();
  }
}

//将候选者添加到呼叫方
const handleCallerIceEvent = (event) => {
  if (event.candidate) {
    log(`正在向接收方添加候选者：${event.candidate.candidate}`);

    receiverPC
        .addIceCandidate(new RTCIceCandidate(event.candidate))
        .catch((err) => log(`向接收方添加候选者时出错：${err}`));
  } else {
    log("呼叫方没有更多的候选者。");
  }
}

//向接收方添加候选者
const handleReceiverIceEvent = (event) => {
  if (event.candidate) {
    log(`Adding candidate to caller: ${event.candidate.candidate}`);

    callerPC
        .addIceCandidate(new RTCIceCandidate(event.candidate))
        .catch((err) => log(`向呼叫方添加候选时出错：${err}`));
  } else {
    log("接收方已经没有更多的候选者。");
  }
}

//向接收方添加媒体
function handleReceiverTrackEvent(event) {
  audio.srcObject = event.streams[0];
}

function handleReceiverAddStreamEvent(event) {
  audio.srcObject = event.stream;
}


//在连接打开后进行拨号
const handleCallerIceConnectionStateChange = () => {
  log(`呼叫方连接状态已更改为 ${callerPC.iceConnectionState}`);
  if (callerPC.iceConnectionState === "connected") {
    log(`发送 DTMF: "${dialString}"`);
    dtmfSender.insertDTMF(dialString, 400, 50);
  }
}

//协商连接
const handleCallerNegotiationNeeded = () => {
  log("协商中……");
  callerPC
      .createOffer(offerOptions)
      .then((offer) => {
        log(`设置呼叫方的本地描述：${offer.sdp}`);
        return callerPC.setLocalDescription(offer);
      })
      .then(() => {
        log("将接收方的远程描述设置为与呼叫方的本地描述相同");
        return receiverPC.setRemoteDescription(callerPC.localDescription);
      })
      .then(() => {
        log("创建应答");
        return receiverPC.createAnswer();
      })
      .then((answer) => {
        log(`将接收方的本地描述设置为 ${answer.sdp}`);
        return receiverPC.setLocalDescription(answer);
      })
      .then(() => {
        log("设置要匹配的呼叫方的远程描述");
        return callerPC.setRemoteDescription(receiverPC.localDescription);
      })
      .catch((err) => log(`协商过程中出错：${err.message}`));
}

//跟踪其他状态变化
const handleCallerSignalingStateChangeEvent = () => {
  log(`呼叫方的信令状态已更改为 ${callerPC.signalingState}`);
}
const handleCallerGatheringStateChangeEvent = () => {
  log(`呼叫方的 ICE 收集状态已更改为 ${callerPC.iceGatheringState}`);
}

function log(msg) {
  logElement.innerHTML += `${msg}<br/>`;
}


onMounted(() => {
  initWebSocket()

  //初始化
  logElement = document.querySelector(".log");
  dialButton = document.querySelector("#dial");
  dialButton.addEventListener("click", connectAndDial, false);
})

onBeforeUnmount(() => {
  disableWebSocket()
})
</script>

<style scoped>
.homepage-hero-module,
.video-container {
  position: relative;
  height: 100vh;
  overflow: hidden;
}

.video-container .poster img {
  z-index: 0;
  position: absolute;
}

.video-container .filter {
  z-index: 1;
  position: absolute;
  /*background: rgba(0, 0, 0, 0.4);*/
  width: 100%;
}

.fillWidth {
  width: 100%;
}
</style>
